package com.ruoyi.common.utils.modbus;

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 带线程追踪的Modbus连接管理工具类，支持强制关闭关联线程和连接
 */
public class ModbusConnectionManager {
    private static final Logger log = LoggerFactory.getLogger(ModbusConnectionManager.class);

    // 静态常量
    private static final long CONNECTION_TIMEOUT_MS = 3 * 1000;
    private static final long CLEANUP_INTERVAL_MS = 2 * 1000;
    private static final long THREAD_INTERRUPT_WAIT_MS = 1000; // 线程中断等待时间

    // 全局连接映射 - 使用ConcurrentHashMap保证基础线程安全
    private static final Map<String, MasterEntry> GLOBAL_MASTER_MAP = new ConcurrentHashMap<>();

    // 全局重入锁
    private static final ReentrantLock GLOBAL_LOCK = new ReentrantLock();

    // 内部类：存储连接信息和关联线程
    static class MasterEntry {
        ModbusMaster master;
        long lastUsedTime;
        volatile boolean isClosed;
        String host;
        int port;
        // 线程追踪信息
        final Set<Thread> associatedThreads = ConcurrentHashMap.newKeySet();
        String createThreadName;
        final ReentrantLock entryLock = new ReentrantLock();

        MasterEntry(ModbusMaster master, String host, int port) {
            this.master = master;
            this.lastUsedTime = System.currentTimeMillis();
            this.isClosed = false;
            this.host = host;
            this.port = port;
            this.createThreadName = Thread.currentThread().getName();
            // 记录创建线程
            associatedThreads.add(Thread.currentThread());
        }

        /**
         * 记录使用该连接的线程
         */
        void trackUsingThread() {
            if (!isClosed) {
                associatedThreads.add(Thread.currentThread());
            }
        }

        /**
         * 移除已结束的线程追踪
         */
        void cleanUpFinishedThreads() {
            associatedThreads.removeIf(t -> !t.isAlive());
        }

        /**
         * 中断所有关联的活跃线程
         */
        void interruptAssociatedThreads() {
            cleanUpFinishedThreads();
            if (!associatedThreads.isEmpty()) {
                log.warn("连接[{}:{}]仍有关联线程({}个)，将尝试中断",
                        host, port, associatedThreads.size());

                for (Thread thread : associatedThreads) {
                    if (thread.isAlive() && !thread.isInterrupted()) {
                        try {
                            log.debug("中断线程: {} (ID: {})", thread.getName(), thread.getId());
                            thread.interrupt();
                        } catch (SecurityException e) {
                            log.error("无法中断线程: {} (ID: {})", thread.getName(), thread.getId(), e);
                        }
                    }
                }
            }
        }

        void updateLastUsedTime() {
            if (!isClosed) {
                this.lastUsedTime = System.currentTimeMillis();
            }
        }

        boolean isExpired() {
            return !isClosed && (System.currentTimeMillis() - lastUsedTime > CONNECTION_TIMEOUT_MS);
        }

        /**
         * 强制关闭连接，包括中断关联线程
         */
        void forceClose() {
            // 先尝试正常关闭
            if (entryLock.tryLock()) {
                try {
                    if (isClosed || master == null) {
                        return;
                    }

                    // 1. 中断关联线程
                    interruptAssociatedThreads();

                    // 2. 等待线程中断
                    try {
                        Thread.sleep(THREAD_INTERRUPT_WAIT_MS);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }

                    // 3. 销毁连接
                    master.destroy();

                    // 4. 增强关闭TCP连接
                    if (master instanceof TcpMaster) {
                        enhancedCloseTcpMaster((TcpMaster) master);
                    }

                    isClosed = true;
                    log.debug("Modbus连接[{}:{}]已强制关闭 (线程: {})",
                            host, port, Thread.currentThread().getName());
                } catch (Exception e) {
                    log.error("强制关闭Modbus连接[{}:{}]时发生错误", host, port, e);
                } finally {
                    entryLock.unlock();
                }
            } else {
                log.warn("无法获取连接[{}:{}]的锁，将进行强制清理", host, port);
                // 强制标记为关闭
                isClosed = true;
                // 直接中断所有关联线程
                interruptAssociatedThreads();
            }
        }

        boolean tryLock() {
            if (isClosed) {
                return false;
            }
            boolean locked = entryLock.tryLock();
            if (locked) {
                trackUsingThread(); // 记录使用线程
            }
            return locked;
        }

        void unlock() {
            if (entryLock.isHeldByCurrentThread()) {
                entryLock.unlock();
            }
        }

        private void enhancedCloseTcpMaster(TcpMaster tcpMaster) {
            // 尝试调用各种可能的关闭方法
            boolean methodClosed = tryCloseByReflection(tcpMaster, "close");
            if (!methodClosed) methodClosed = tryCloseByReflection(tcpMaster, "disconnect");
            if (!methodClosed) methodClosed = tryCloseByReflection(tcpMaster, "shutdown");

            // 尝试关闭各种可能的socket字段
            if (!methodClosed) {
                boolean fieldClosed = tryCloseSocketByReflection(tcpMaster, "socket");
                if (!fieldClosed) fieldClosed = tryCloseSocketByReflection(tcpMaster, "clientSocket");
                if (!fieldClosed) fieldClosed = tryCloseSocketByReflection(tcpMaster, "connection");
            }
        }

        private boolean tryCloseByReflection(TcpMaster tcpMaster, String methodName) {
            try {
                Method closeMethod = TcpMaster.class.getDeclaredMethod(methodName);
                closeMethod.setAccessible(true);
                closeMethod.invoke(tcpMaster);
                log.debug("通过反射调用{}()方法关闭连接[{}:{}]", methodName, host, port);
                return true;
            } catch (NoSuchMethodException e) {
                log.trace("TcpMaster中没有{}()方法[{}:{}]", methodName, host, port);
            } catch (Exception e) {
                log.error("反射调用{}()方法失败[{}:{}]", methodName, host, port, e);
            }
            return false;
        }

        private boolean tryCloseSocketByReflection(TcpMaster tcpMaster, String fieldName) {
            try {
                Field socketField = TcpMaster.class.getDeclaredField(fieldName);
                socketField.setAccessible(true);
                Object fieldValue = socketField.get(tcpMaster);

                if (fieldValue instanceof Socket) {
                    Socket socket = (Socket) fieldValue;
                    if (!socket.isClosed()) {
                        socket.close();
                        log.debug("通过反射获取{}字段并关闭socket[{}:{}]", fieldName, host, port);
                        return true;
                    }
                }
            } catch (NoSuchFieldException e) {
                log.trace("TcpMaster中没有{}字段[{}:{}]", fieldName, host, port);
            } catch (Exception e) {
                log.error("反射处理{}字段失败[{}:{}]", fieldName, host, port, e);
            }
            return false;
        }
    }

    // 定时清理线程
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final AtomicBoolean cleanupStarted = new AtomicBoolean(false);

    // Modbus工厂
    private final ModbusFactory modbusFactory;

    // 单例实例
    private static volatile ModbusConnectionManager instance;

    private ModbusConnectionManager() {
        if (instance != null) {
            throw new IllegalStateException("ModbusConnectionManager已被实例化，请使用getInstance()获取");
        }
        this.modbusFactory = new ModbusFactory();
        initCleanupTask();
    }

    public static ModbusConnectionManager getInstance() {
        if (instance == null) {
            synchronized (ModbusConnectionManager.class) {
                if (instance == null) {
                    instance = new ModbusConnectionManager();
                }
            }
        }
        return instance;
    }

    private String generateKey(String host, int port) {
        return host.trim().toLowerCase() + ":" + port;
    }

    private void initCleanupTask() {
        if (cleanupStarted.compareAndSet(false, true)) {
            scheduler.scheduleAtFixedRate(() -> {
                GLOBAL_LOCK.lock();
                try {
                    cleanupExpiredConnections();
                } finally {
                    GLOBAL_LOCK.unlock();
                }
            }, CLEANUP_INTERVAL_MS, CLEANUP_INTERVAL_MS, TimeUnit.MILLISECONDS);

            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                log.info("JVM关闭，强制关闭所有Modbus连接");
                scheduler.shutdown();
                try {
                    if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) {
                        scheduler.shutdownNow();
                    }
                    forceCloseAllConnections();
                } catch (InterruptedException e) {
                    scheduler.shutdownNow();
                }
            }, "ModbusConnectionShutdownHook"));
        }
    }

    public void cleanupExpiredConnections() {
        log.debug("开始清理过期连接，当前连接数: {}", GLOBAL_MASTER_MAP.size());
        List<String> expiredKeys = new ArrayList<>();

        for (Map.Entry<String, MasterEntry> entry : GLOBAL_MASTER_MAP.entrySet()) {
            if (entry.getValue().isExpired()) {
                expiredKeys.add(entry.getKey());
            }
        }

        for (String key : expiredKeys) {
            MasterEntry entry = GLOBAL_MASTER_MAP.remove(key);
            if (entry != null) {
                entry.forceClose();
                log.info("已释放过期连接: {} (创建线程: {})", key, entry.createThreadName);
            }
        }
        log.debug("清理完成，剩余连接数: {}", GLOBAL_MASTER_MAP.size());
    }

    public void closeConnection(String host, int port) {
        String key = generateKey(host, port);
        closeConnection(key);
    }

    public void closeConnection(String key) {
        GLOBAL_LOCK.lock();
        try {
            MasterEntry entry = GLOBAL_MASTER_MAP.remove(key);
            if (entry != null) {
                entry.forceClose();
                log.info("已手动关闭连接: {} (当前线程: {})",
                        key, Thread.currentThread().getName());
            } else {
                log.warn("尝试关闭不存在的连接: {}", key);
            }
        } finally {
            GLOBAL_LOCK.unlock();
        }
    }

    /**
     * 强制关闭所有连接，包括中断关联线程
     */
    public void forceCloseAllConnections() {
        log.info("准备强制关闭所有连接，当前线程: {}", Thread.currentThread().getName());

        GLOBAL_LOCK.lock();
        try {
            log.info("开始强制关闭所有连接，总数: {}", GLOBAL_MASTER_MAP.size());

            List<MasterEntry> allEntries = new ArrayList<>(GLOBAL_MASTER_MAP.values());
            GLOBAL_MASTER_MAP.clear();

            for (MasterEntry entry : allEntries) {
                entry.forceClose();
                log.info("已强制关闭连接: {}:{}", entry.host, entry.port);
            }

            log.info("所有连接强制关闭完成");
        } finally {
            GLOBAL_LOCK.unlock();
        }
    }

    public ModbusMaster getConnection(String host, int port) throws ModbusInitException {
        String key = generateKey(host, port);
        log.debug("尝试获取连接: {} (当前线程: {})", key, Thread.currentThread().getName());

        MasterEntry entry = GLOBAL_MASTER_MAP.get(key);
        if (entry != null) {
            if (entry.tryLock()) {
                try {
                    if (entry.isClosed) {
                        log.warn("连接 {} 已关闭，将创建新连接", key);
                        GLOBAL_MASTER_MAP.remove(key);
                    } else {
                        entry.updateLastUsedTime();
                        log.debug("复用已有连接: {}", key);
                        return entry.master;
                    }
                } finally {
                    entry.unlock();
                }
            } else {
                log.debug("连接 {} 正在使用中，等待释放", key);
                return createNewConnection(host, port, key);
            }
        }

        return createNewConnection(host, port, key);
    }

    private ModbusMaster createNewConnection(String host, int port, String key) throws ModbusInitException {
        GLOBAL_LOCK.lock();
        try {
            MasterEntry existing = GLOBAL_MASTER_MAP.get(key);
            if (existing != null && !existing.isClosed) {
                existing.updateLastUsedTime();
                log.debug("并发场景下复用已有连接: {}", key);
                return existing.master;
            }

            IpParameters params = new IpParameters();
            params.setHost(host);
            params.setPort(port);

            ModbusMaster master = modbusFactory.createTcpMaster(params, true);
            master.setTimeout(1000);
            master.init();

            MasterEntry newEntry = new MasterEntry(master, host, port);
            GLOBAL_MASTER_MAP.put(key, newEntry);
            log.info("创建新连接: {} (线程: {})", key, Thread.currentThread().getName());

            return master;
        } finally {
            GLOBAL_LOCK.unlock();
        }
    }

    // 其他辅助方法
    public void releaseConnection(String host, int port) {
        closeConnection(host, port);
    }

    public int getActiveConnectionCount() {
        return GLOBAL_MASTER_MAP.size();
    }

    public boolean isConnectionExists(String host, int port) {
        String key = generateKey(host, port);
        MasterEntry entry = GLOBAL_MASTER_MAP.get(key);
        return entry != null && !entry.isClosed;
    }

    /**
     * 获取连接关联的线程信息，用于调试
     */
    public String getConnectionThreadsInfo(String host, int port) {
        String key = generateKey(host, port);
        MasterEntry entry = GLOBAL_MASTER_MAP.get(key);
        if (entry == null) {
            return "连接不存在";
        }

        entry.cleanUpFinishedThreads();
        StringBuilder sb = new StringBuilder();
        sb.append("连接[")
                .append(host).append(":").append(port)
                .append("]关联线程(").append(entry.associatedThreads.size()).append("个): ");

        for (Thread t : entry.associatedThreads) {
            sb.append("{name: ").append(t.getName())
                    .append(", id: ").append(t.getId())
                    .append(", alive: ").append(t.isAlive())
                    .append("}, ");
        }
        return sb.toString();
    }
}
